/* eslint-disable vars-on-top */
/*
 pivot
https://www.fmz.com/bbs-topic/4200
https://www.fmz.com/bbs-topic/485
按周期分组：
##过滤信号
##仓位
##止损
##止盈
做多仓位=总户总资金*可承受单笔亏损率/（开仓价格-止损价格）
数量：止损*系数，拥有的钱
止损价：低点(7个k线低点)-0.5ATR

### 止盈的判断
- 普通信号-追踪止盈：止盈市价0.5%，最高点回撤0.1%止盈：100，100.5，100.6-0.1006=100.4994
- 标准信号：信号发送2个阶梯，每个1/3，最后1/3人工操作

### 转发示例
`
http://site.fanglianhang.com/toGet.aspx
{
"#baseUrl":"http://www.fmz.com/api/v1?",
"method":"CommandRobot",
"access_key":"08bd3ccd22c34d5a60d5eb60bc976474",
"secret_key":"886f676ea4eaa16654e7a61db006d8e4",
"args":"[232361,\"buy_closesell2:{\\\"name\\\":\\\"BTC_USDT\\\",\\\"period\\\":180,\\\"sells\\\":[111,222]}\"]"
}
`
 */

//if (require) {
const { _ } = require('../lib/underscore')
//}


/** 杠杆 */
var lever$ = 20
//趋势
const trendDic$ = {}
//止损,止盈，orderid,direction,amount,loss,sell
/** @type {{ orderid: any, direction: string, amount: any, loss: any, sell: any,buyPrice: any ,topPrice: any,remark: any}[]} */
var sellPositionDic$ = []
/** 止损率 */
const stopLossRatio$ = 0.02
/** 止盈率,单倍 */
const sellDownRatio$ = 0.002 //0.005
/** 回撤率-止盈 */
const stopProfitSlide$ = 0.0002 //0.001

/**
 * @param {number} buyAm
 */
const buyAmountXs = (buyAm) => {
    return buyAm * 0.5
}
// eslint-disable-next-line no-unused-vars
function main () {
    //分析命令： {name:'',amount:0.0,price:-1.0,ts:0,period:60}
    //调用相应方法setTrend,buy,buy_closesell,sell,sell_closebuy
    //趋势命令：trend:{ name = '', isUp = 0, period = 0 }
    //买入命令：buy:{ name = '',direction='closesell',period = 60 ,sell=0}
    if (IsVirtual()) {
        Log('测试环境！')
    }
    Log('账户基本信息：', exchange.GetAccount())
    Sleep(1000)
    do {
        let a = 'a'
        // try {
        AutoSell()
        Sleep(1000)
        let cmd = GetCommand()
        if (IsVirtual()) {

            // if (testNum = 0)
            //     cmd = `{ "name" :'etc',period : 60 }`
        }
        if (!cmd) {
            continue
        }
        Log('命令：', cmd)
        if (cmd.indexOf('cc:') == 0) {
            eval(cmd.slice(3))
            continue
        }
        //cmdCall(cmd)
        cmdCallLite(cmd)
        // } catch (error) {
        //     Log("发生异常：", error)
        // }
    } while (1)
}

/**
 * @param {string} cmd
 */
function cmdCall (cmd) {
    let { cmdName, argObj } = getCmdInfo(cmd)
    if (cmdName === 'trend') {
        saveTrend(argObj)
    } else if (cmdName === 'buy') {
        buy(argObj)
    } else if (cmdName === 'buy_closesell') {
        buy_closesell(argObj)
    }
}

/**
 * @param {string} cmd
 */
function cmdCallLite (cmd) {
    let prd = 60 * 5
    let { cmdName, argObj } = getCmdInfo(cmd)
    if (cmdName === 'trend1') {
        saveTrend({ 'name': 'BTC_USDT', 'isUp': 1, 'period': prd })
    } else if (cmdName === 'trend-1') {
        saveTrend({ 'name': 'BTC_USDT', 'isUp': -1, 'period': prd })
    } else if (cmdName === 'trend2') {
        saveTrend({ 'name': 'BTC_USDT', 'isUp': 2, 'period': prd })
    } else if (cmdName === 'buy') {
        buy(argObj)
    } else if (cmdName === 'buy_closesell') {
        buy_closesell(argObj)
    }
}
/**
   * @param {string} cmd
   */
function getCmdInfo (cmd) {
    let slIndex = cmd.indexOf(':')
    let cmdName = slIndex > 0 ? cmd.slice(0, slIndex) : ''
    if (cmdName === '') {
        return { 'cmdName': cmd, 'argObj': '' }
    }
    let cmdArg = cmd.slice(slIndex + 1)
    let argObj = JSON.parse(cmdArg)
    return { cmdName, argObj }
    //简单参数化结构：aa!1!2!3~bb!1
}

/** 根据止损止盈字典，进行卖出
 *
 */
function AutoSell () {
    let tck = exchange.GetTicker()
    let nowBuyP = tck.Buy
    let nowLastP = tck.Last
    let nullItemCount = 0
    sellPositionDic$.forEach((q, i) => {
        if (_.isEmpty(q)) {
            nullItemCount++
            return
        }
        let isRunSell = false
        if (q.direction === 'closesell') {
            isRunSell = stopByClosesell(q)
        } else if (q.direction === 'closebuy') {
            isRunSell = stopByCloseBuy(q)
        }
        if (isRunSell) {
            Log('清除1个止损止盈记录：', sellPositionDic$[i])
            sellPositionDic$[i] = null
            nullItemCount++
        }
    })
    if (nullItemCount) {
        sellPositionDic$ = sellPositionDic$.filter(p => !_.isEmpty(p))
    }

    /**  平多单,返回true表示执行卖出成功
     * @param {{ orderid: any, direction: any, amount: string | number, loss: number, sell: any[],buyPrice: number,topPrice: number }} q
     * @returns {boolean|undefined} 返回true表示执行卖出成功
    */
    function stopByCloseBuy (q) {
        //止损
        if (nowLastP <= q.loss) {
            return sellDown(q, '止损')
        }
        //止盈,固定
        /*
        1.止损（7根k最小值）,止盈（给定值）
        2.当达到第一止盈时：卖出1/3，止损不变
        3.当达到第二止盈时：卖出1/3，止损改为买入价
        4.等待最后1/3止损，不管止盈
        */
        if (q.sell) {
            if (q.sell.length === 1) {
                let stopProfit = q.sell[0]
                if (nowLastP >= stopProfit) {
                    return sellDown(q, '固定止盈')
                }
                return
            } q.sell.length === 2
            {
                if (q.sell.sellNode === 2) {
                    return
                }
                let stopProfit = q.sell[q.sell.sellNode]
                if (nowLastP >= stopProfit) {

                    if (q.sell.sellNode === 0) {
                        let sellq = Object.assign({}, q)
                        sellq.amount = sellq.amount / 3
                        let re = sellDown(sellq, '分级止盈' + q.sell.sellNode)
                        if (!re) {
                            return
                        }
                        q.sell.sellNode++
                        q.remark += ';止盈0成交-原仓位:' + q.amount
                        q.amount = q.amount * 2 / 3
                        return
                    } else if (q.sell.sellNode === 1) {
                        let sellq = Object.assign({}, q)
                        sellq.amount = sellq.amount / 2
                        let re = sellDown(sellq, '分级止盈' + q.sell.sellNode)
                        if (!re) {
                            return
                        }
                        q.sell.sellNode++
                        q.remark += ';止盈1成交-原仓位:' + q.amount
                        q.amount = q.amount / 2
                        q.loss = q.buyPrice
                        Log('分级止盈1调整止损:', { q })
                        return
                    }
                } else {
                    return
                }

            }
        }

        //滑动止盈， //当前市场价是否>增长比率，大于->当前价与最高价相差是否超过回撤比率，超过止盈
        //求利润率：卖出/买入-1,买空单*-1
        let nowRate = calcProfitRate(nowLastP, q.buyPrice)
        if (q.topPrice <= nowRate) {
            q.topPrice = nowRate
            return
        }
        if (q.topPrice < sellDownRatio$) {
            return
        }
        if (q.topPrice * (1 - stopProfitSlide$) > nowRate) {
            return sellDown(q, '回撤止盈')
        }
    }
    /**
     * @param {{orderid?: any;direction: any;amount: any;loss?: any;sell?: any;buyPrice?: any;topPrice?: any;}} q readonly
     * @param {string | undefined} [remark]
     */
    function sellDown (q, remark) {
        exchange.SetDirection(q.direction)
        Log('准备卖出，当前情况：', { remark, tck, 'sellItem_q': q })
        let id = null
        if (q.direction === 'closebuy') {
            id = exchange.Sell(-1, q.amount)
        } //nowBuyP
        else if (q.direction === 'closesell') {
            id = exchange.Buy(-1, q.amount)
        } else {
            Log('卖出方向参数错误,忽略：', q)
            return true
        }
        if (id) {
            exchange.CancelOrder(id)
            return true
        }
        Log('卖出失败,卖出信息:', { remark, nowBuyP, q })

    }
    /** 单位利润
     * @param {Number} nowPrice 当前价
     * @param {Number} buyPrice 买入价
     * @param {Boolean} direction 是否多单，默认多单
     */
    function calcUnitProfit (nowPrice, buyPrice, direction = true) {

        return direction ? nowPrice - buyPrice : -(nowPrice - buyPrice)
        if (direction) {
            return nowPrice - buyPrice
        }
        return -(nowPrice - buyPrice)

    }
    /** 单位利润率，负值表示亏损
     * @param {Number} nowPrice 当前价
     * @param {Number} buyPrice 买入价
     * @param {Boolean} direction 是否多单，默认多单
     */
    function calcProfitRate (nowPrice, buyPrice, direction = true) {
        return direction ? nowPrice / buyPrice - 1 : -(nowPrice / buyPrice - 1)
    }
    /**
     * @param {{ sell: number; buyPrice: number; loss: number; direction: string; amount: number; }} q
     * @param {string | number} i
     */
    function stopBySell2 (q, i) {
        let stopProfit = q.sell > 0 ? q.sell : q.buyPrice * (1 - sellDownRatio$)
        if (nowLastP >= q.loss || nowLastP <= stopProfit) {
            exchange.SetDirection(q.direction)
            let id = exchange.Sell(nowBuyP, q.amount)
            if (id) {
                exchange.CancelOrder(id)
                sellPositionDic$[i] = null
            } else {
                Log('卖出失败,卖出价:' + nowBuyP, q)
            }
        }
        4444
    }

    /** 平空单，返回true表示执行卖出成功
     * @param {{ orderid: any, direction: any, amount: string | number, loss: number, sell: any[],buyPrice: number,topPrice: number }} q
     * @returns {boolean} 返回true表示执行卖出成功
    */
    function stopByClosesell (q) {
        //止损
        if (nowLastP >= q.loss) {
            return sellDown(q)
        }
        //止盈,固定
        //let isFixed = q.sell > 0
        if (q.sell) {
            if (q.sell.length === 1) {
                let stopProfit = q.sell[0]
                if (nowLastP <= stopProfit) {
                    return sellDown(q, '固定止盈')
                }
                return
            } q.sell.length === 2
            {
                if (q.sell.sellNode === 2) {
                    return
                }
                let stopProfit = q.sell[q.sell.sellNode]
                if (nowLastP <= stopProfit) {

                    if (q.sell.sellNode === 0) {
                        let sellq = Object.assign({}, q)
                        sellq.amount = sellq.amount / 3
                        let re = sellDown(sellq, '分级止盈' + q.sell.sellNode)
                        if (!re) {
                            return
                        }
                        q.sell.sellNode++
                        q.remark += ';止盈0成交-原仓位:' + q.amount
                        q.amount = q.amount * 2 / 3
                        return
                    } else if (q.sell.sellNode === 1) {
                        let sellq = Object.assign({}, q)
                        sellq.amount = sellq.amount / 2
                        let re = sellDown(sellq, '分级止盈' + q.sell.sellNode)
                        if (!re) {
                            return
                        }
                        q.sell.sellNode++
                        q.remark += ';止盈1成交-原仓位:' + q.amount
                        q.amount = q.amount / 2
                        q.loss = q.buyPrice
                        Log('分级止盈1调整止损:', { q })
                        return
                    }
                } else {
                    return
                }

            }
        }
        //滑动止盈， //当前市场价是否>增长比率，大于->当前价与最高价相差是否超过回撤比率，超过止盈
        //求利润率：卖出/买入-1,买空单*-1
        let nowRate = calcProfitRate(nowLastP, q.buyPrice, false)
        if (q.topPrice <= nowRate) {
            q.topPrice = nowRate
            return
        }
        if (q.topPrice < sellDownRatio$) {
            return
        }
        if (q.topPrice * (1 - stopProfitSlide$) > nowRate) {
            return sellDown(q)
        }
    }
}

function addSellDic ({ orderid, direction, amount, loss, sells, buyPrice }) {
    if (Array.isArray(sells) && sells.length === 1 && sells[0] > 0) {
        sellPositionDic$.push({ orderid, direction, 'amount': amount, loss, 'sell': sells[0], buyPrice, 'topPrice': 0, 'remark': '固定止3' })
        Log('添加止损，固定止盈：', { orderid, direction, amount, loss, sells, buyPrice, 'topPrice': 0 })
    } else if (Array.isArray(sells) && sells.length >= 2 && sells[0] > 0 && sells[1] > 0) {
        //var amount3 = amount / 3
        sells.sellNode = 0
        sellPositionDic$.push({ orderid, direction, amount, loss, 'sell': sells, buyPrice, 'topPrice': 0, 'remark': '分段止' })
        // sellPositionDic$.push({ orderid, direction, amount: amount3, loss, sell: sells[1], buyPrice, topPrice: 0, remark: "分段止2" })
        // let maxSell=direction==="closebuy"?123456789:0
        // sellPositionDic$.push({ orderid, direction, amount: (amount-amount3*2), loss, sell: maxSell, buyPrice, topPrice: 0, remark: "分段止3" })
        Log('添加止损，分段止盈：', { orderid, direction, amount, loss, 'sells': sells, buyPrice, 'topPrice': 0, amount })
    } else {
        sellPositionDic$.push({ orderid, direction, amount, loss, 'sell': 0, buyPrice, 'topPrice': 0, 'remark': '回撤止' })
        Log('添加止损，回撤止盈：', { orderid, direction, amount, loss, sells, buyPrice, 'topPrice': 0 })
    }

}

function buy_closesell ({ name = '', amount = 0.0, price = -1.0, ts = 0, period = 0, sells = [] }) {
    if (_.isEmpty(name) || period < 1) {
        Log('参数异常,放弃交易', { name, period })
        return
    }
    //过滤，查找趋势记录
    let trend = trendDic$[getTrendDicKey(name, period)]
    if (!(trend === -1 || trend === 2)) {
        Log('趋势不对，忽略消息', { trend })
        return
    }
    //仓位计算:获取总资产，单笔亏损率，止损
    let account = exchange.GetAccount()
    let lossV = stopLossRatio$
    let records = exchange.GetRecords(period)
    if (records.length < 1) {
        Log('buy:历史数据查询失败,无法计算止损')
        return
    }
    let max = records[0] //{}
    max = {}
    records.slice(-7).map((p, i) => {
        if (_.isEmpty(max) || max.High < p.High) {
            max = p
            max.index = i
        }
    })
    let atr = TA.ATR(records.slice(-14), period).slice(-1)[0] //买空单：最近7根k最高点+0.5atr
    let stopLoss = max.High + atr * 0.5
    let tck = exchange.GetTicker()
    if (_.isEmpty(tck)) {
        Log('行情获取失败,放弃交易')
        return
    }
    Log('实时行情:', tck)
    Log('atr,k_min', { atr, max })
    let nowPrice = tck.Buy
    let buyAmount = account.Balance * lossV / (stopLoss - nowPrice) //止损-建仓
    buyAmount = buyAmountXs(buyAmount)
    Log('准备买入仓位account.Balance * lossV / (nowPrice - stopLoss):', { buyAmount, 'Balance': account.Balance, lossV, nowPrice, 'sellDown': stopLoss })
    if (!(buyAmount > 0)) {
        Log('放弃交易,购买量错误：', buyAmount)
        return
    }
    //交易
    // 设置下单类型为做空
    exchange.SetContractType('swap')
    exchange.SetDirection('sell')
    exchange.SetMarginLevel(lever$) //20
    exchange.SetPrecision(4, 3)
    let id = exchange.Sell(-1, buyAmount)
    if (id) {
        addSellDic({ 'orderid': id, 'direction': 'closesell', 'amount': buyAmount, 'loss': stopLoss, 'sells': sells, 'buyPrice': nowPrice })

    } else {
        Log('做空单失败:', { buyAmount, id })
    }
}

function buy ({ name = '', amount = 0.0, price = -1.0, ts = 0, period = 0, sells = [] }) {
    if (_.isEmpty(name) || period < 1) {
        Log('参数异常,放弃交易', { name, period })
        return
    }
    //过滤，查找趋势记录
    let trend = trendDic$[getTrendDicKey(name, period)]

    if (!(trend === 1 || trend === 2)) {
        Log('趋势不对，忽略消息', { trend })
        return
    }
    //仓位计算:获取总资产，单笔亏损率，止损
    let account = exchange.GetAccount()
    Log('账户信息查询：', account)
    let lossV = stopLossRatio$
    let records = exchange.GetRecords(period)
    if (records.length < 1) {
        Log('buy:历史数据查询失败,无法计算止损,放弃交易')
        return
    }
    let minLow = {}
    Log('近7个周期历史：', records.slice(-7))
    //买空单：最近7根k最低高点-0.5atr，atr取当前
    records.slice(-7).map((p, i) => {
        if (_.isEmpty(minLow) || minLow.Low > p.Low) {
            minLow = p
            minLow.index = i
        }
    })

    let atr = TA.ATR(records.slice(-14), period).slice(-1)[0] //[minLow.index] //取当前
    let stopLoss = minLow.Low - atr * 0.5
    let tck = exchange.GetTicker()
    if (_.isEmpty(tck)) {
        Log('行情获取失败,放弃交易')
        return
    }
    Log('实时行情:', tck)
    Log('atr,k_min', { atr, minLow })
    let nowPrice = tck.Buy
    let buyAmount = account.Balance * lossV / (nowPrice - stopLoss)
    buyAmount = buyAmountXs(buyAmount)
    Log('准备买入仓位account.Balance * lossV * (nowPrice - stopLoss):', { buyAmount, 'Balance': account.Balance, lossV, nowPrice, 'sellDown': stopLoss })

    if (!(buyAmount > 0)) {
        Log('放弃交易,购买量错误：', buyAmount)
        return
    }

    //交易
    // 设置下单类型为做多
    exchange.SetContractType('swap')
    exchange.SetDirection('buy')
    exchange.SetMarginLevel(lever$)
    exchange.SetPrecision(4, 3)
    let id = exchange.Buy(-1, buyAmount)
    if (id) {
        addSellDic({ 'orderid': id, 'direction': 'closebuy', 'amount': buyAmount, 'loss': stopLoss, 'sells': sells, 'buyPrice': nowPrice })
    } else {
        Log('做多单失败:', { buyAmount, id })
    }
}


function saveTrend ({ name = '', isUp = 0, period = 0 }) {
    if (!(name && period > 0 && (isUp === 1 || isUp === -1 || isUp === 2))) {
        Log('趋势设置错误：', { name, isUp, period })
        return false
    }
    trendDic$[getTrendDicKey(name, period)] = isUp
    return true
}


function getTrendDicKey (name = '', period = 0) {
    return `${ name }-${ period }`
}


function init () {
    let dic = _G('sellPositionDic$')
    if (dic) { // @ts-ignore
        sellPositionDic$ = dic
        Log(`初始化${ GetPid() }:`, { sellPositionDic$ })
    }
    let ct = exchange.SetContractType('swap')
    Log('合约类型', { ct })
    exchange.SetPrecision(4, 3)
}
function _save () {
    _G('sellPositionDic$', sellPositionDic$)
    Log(`持久化保存${ GetPid() }:`, { sellPositionDic$ })
}
function onexit () {
    _save()
}
// @ts-ignore
function onerror () {
    _save()
}
